// +FHDR------------------------------------------------------------
//                 Copyright (c) 2022 .
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : apb_driver.sv
// Author        : 
// Created On    : 2022-08-25 15:07
// Last Modified : 
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------

`ifndef __APB_DRIVER_SV__
`define __APB_DRIVER_SV__

class apb_driver extends uvm_driver #(apb_transaction);

	virtual apb_interface vif;
    `ifdef UTILS_TEST
    uvm_analysis_port #(apb_transaction) ap;
    `endif
	`uvm_component_utils(apb_driver)

	extern function new(string name = "apb_driver", uvm_component parent=null);
	extern virtual function void build_phase(uvm_phase phase);
	extern virtual task run_phase(uvm_phase phase);
	extern virtual task drive_pkt(ref apb_transaction pkt);
	extern virtual task drive_write(ref apb_transaction pkt);
	extern virtual task drive_read(ref apb_transaction pkt);
	extern virtual task drive_idle();

endclass: apb_driver

function apb_driver::new(string name = "apb_driver", uvm_component parent=null);
	super.new(name, parent);
endfunction: new

function void apb_driver::build_phase(uvm_phase phase);
	super.build_phase(phase);
	`uvm_info("apb_driver", "build_phase is called", UVM_HIGH);
	if(!uvm_config_db #(virtual apb_interface)::get(this, "", "vif", vif))begin
		`uvm_fatal("apb_driver", "virtual interface get fatal");
	end
    `ifdef UTILS_TEST
    ap   = new("ap", this);
    `endif
endfunction: build_phase

task apb_driver::run_phase(uvm_phase phase);
    super.run_phase(phase);
	phase.raise_objection(this);
	`uvm_info("apb_driver", "run_phase is called", UVM_LOW);
    this.drive_idle();
    while(!this.vif.presetn) @(posedge vif.pclk);
    
    @this.vif.master_cb;
	while(1)begin
        apb_transaction req;
		seq_item_port.try_next_item(req);
		//seq_item_port.get_next_item(req);
        if(req == null) begin
        //    this.drive_idle();
        //    continue;
            //`uvm_info("apb_driver", "req == null", UVM_LOW);
            break;
        end
		else begin
	        //`uvm_warning("apb_driver", "need drive a pkt start");
            `ifdef UTILS_TEST
            ap.write(req);
            `endif
			this.drive_pkt(req);
            //`uvm_warning("apb_driver", "drive a pkt over");
			seq_item_port.item_done();
		end
	end
    //`uvm_warning("apb_driver", "out from while 1");
    this.drive_idle();

    #1000;
	phase.drop_objection(this);
endtask: run_phase

task apb_driver::drive_pkt(ref apb_transaction pkt);
    //@this.vif.master_cb;
    case (pkt.kind)
        apb_transaction::WRITE : drive_write(pkt);
        apb_transaction::READ  : drive_read(pkt);
    endcase
    repeat(pkt.cycle_post) this.drive_idle();
endtask: drive_pkt

task apb_driver::drive_write(ref apb_transaction pkt);
    this.vif.master_cb.psel    <= '1;
    this.vif.master_cb.penable <= '0;
    this.vif.master_cb.pwrite  <= '1;
    this.vif.master_cb.pwdata  <= pkt.data;
    this.vif.master_cb.paddr   <= pkt.addr;
    while(1)begin
        if(this.vif.master_cb.pready == 1'b1) break;
        @this.vif.master_cb;
    end
    @this.vif.master_cb;
    this.vif.master_cb.penable <= '1;
    this.drive_idle();
endtask: drive_write

task apb_driver::drive_read(ref apb_transaction pkt);
    this.vif.master_cb.psel    <= '1;
    this.vif.master_cb.penable <= '0;
    this.vif.master_cb.pwrite  <= '0;
    this.vif.master_cb.paddr   <= pkt.addr;
    while(1)begin
        if(this.vif.master_cb.pready == 1'b1) break;
        @this.vif.master_cb;
    end
    @this.vif.master_cb;
    this.vif.master_cb.penable <= '1;
    pkt.data = this.vif.master_cb.prdata;
    this.drive_idle();
endtask: drive_read

task apb_driver::drive_idle();
    @this.vif.master_cb;
	this.vif.master_cb.psel    <= 'd0;
	this.vif.master_cb.penable <= 'd0;
	this.vif.master_cb.pwrite  <= 'd0;
    this.vif.master_cb.paddr   <= 'dx;
    this.vif.master_cb.pwdata  <= 'dx;
    this.vif.master_cb.pstrb   <= 'dx;
endtask: drive_idle

`endif
